package cn.jingyuan.bee.utils;

import cn.jingyuan.bee.utils.collection.IterUtils;
import cn.jingyuan.bee.utils.comparator.CompareUtils;
import cn.jingyuan.bee.utils.convert.Convert;
import cn.jingyuan.bee.utils.exceptions.UtilsException;
import cn.jingyuan.bee.utils.io.FastByteArrayOutputStream;
import cn.jingyuan.bee.utils.io.IoUtils;
import cn.jingyuan.bee.utils.map.MapUtils;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.*;

/**
 * 对象工具类，包括判空、克隆、序列化等操作
 */
public class ObjectUtils {

    /**
     * 比较两个对象是否相等。<br>
     * 相同的条件有两个，满足其一即可：<br>
     * <ol>
     * <li>obj1 == null &amp;&amp; obj2 == null</li>
     * <li>obj1.equals(obj2)</li>
     * </ol>
     * 1. obj1 == null &amp;&amp; obj2 == null 2. obj1.equals(obj2)
     *
     * @param obj1 对象1
     * @param obj2 对象2
     *
     * @return 是否相等
     *
     * @see Objects#equals(Object, Object)
     */
    public static boolean equal(Object obj1, Object obj2) {
        return Objects.equals(obj1, obj2);
    }

    /**
     * 比较两个对象是否不相等。<br>
     *
     * @param obj1 对象1
     * @param obj2 对象2
     *
     * @return 是否不等
     */
    public static boolean notEqual(Object obj1, Object obj2) {
        return !equal(obj1, obj2);
    }

    /**
     * 计算对象长度，如果是字符串调用其 length 函数，集合类调用其 size 函数，数组调用其 length 属性，其他可遍历对象遍历计算长度<br>
     * 支持的类型包括：
     * <ul>
     * <li>CharSequence</li>
     * <li>Map</li>
     * <li>Iterator</li>
     * <li>Enumeration</li>
     * <li>Array</li>
     * </ul>
     *
     * @param obj 被计算长度的对象
     *
     * @return 长度
     */
    public static int length(Object obj) {
        if (obj == null) {
            return 0;
        }
        if (obj instanceof CharSequence) {
            return ((CharSequence) obj).length();
        }
        if (obj instanceof Collection) {
            return ((Collection<?>) obj).size();
        }
        if (obj instanceof Map) {
            return ((Map<?, ?>) obj).size();
        }

        int count;
        if (obj instanceof Iterator) {
            Iterator<?> iter = (Iterator<?>) obj;
            count = 0;
            while (iter.hasNext()) {
                count++;
                iter.next();
            }
            return count;
        }
        if (obj instanceof Enumeration) {
            Enumeration<?> enumeration = (Enumeration<?>) obj;
            count = 0;
            while (enumeration.hasMoreElements()) {
                count++;
                enumeration.nextElement();
            }
            return count;
        }
        if (obj.getClass().isArray()) {
            return Array.getLength(obj);
        }
        return -1;
    }

    /**
     * 对象中是否包含元素<br>
     * 支持的对象类型包括：
     * <ul>
     * <li>String</li>
     * <li>Collection</li>
     * <li>Map</li>
     * <li>Iterator</li>
     * <li>Enumeration</li>
     * <li>Array</li>
     * </ul>
     *
     * @param obj 对象
     * @param element 元素
     *
     * @return 是否包含
     */
    public static boolean contains(Object obj, Object element) {
        if (obj == null) {
            return false;
        }
        if (obj instanceof String) {
            if (element == null) {
                return false;
            }
            return ((String) obj).contains(element.toString());
        }
        if (obj instanceof Collection) {
            return ((Collection<?>) obj).contains(element);
        }
        if (obj instanceof Map) {
            return ((Map<?, ?>) obj).containsValue(element);
        }

        if (obj instanceof Iterator) {
            Iterator<?> iter = (Iterator<?>) obj;
            while (iter.hasNext()) {
                Object o = iter.next();
                if (equal(o, element)) {
                    return true;
                }
            }
            return false;
        }
        if (obj instanceof Enumeration) {
            Enumeration<?> enumeration = (Enumeration<?>) obj;
            while (enumeration.hasMoreElements()) {
                Object o = enumeration.nextElement();
                if (equal(o, element)) {
                    return true;
                }
            }
            return false;
        }
        if (obj.getClass().isArray()) {
            int len = Array.getLength(obj);
            for (int i = 0; i < len; i++) {
                Object o = Array.get(obj, i);
                if (equal(o, element)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 检查对象是否为 null<br>
     * 判断标准为：
     *
     * <pre>
     * 1. == null
     * 2. equals(null)
     * </pre>
     *
     * @param obj 对象
     *
     * @return 是否为 null
     */
    public static boolean isNull(Object obj) {
        return null == obj || obj.equals(null);
    }

    /**
     * 检查对象是否不为 null
     *
     * @param obj 对象
     *
     * @return 是否为 null
     */
    public static boolean isNotNull(Object obj) {
        return null != obj && !obj.equals(null);
    }

    /**
     * 判断指定对象是否为空，支持：
     *
     * <pre>
     * 1. CharSequence
     * 2. Map
     * 3. Iterable
     * 4. Iterator
     * 5. Array
     * </pre>
     *
     * @param obj 被判断的对象
     *
     * @return 是否为空，如果类型不支持，返回 false
     */
    public static boolean isEmpty(Object obj) {
        if (null == obj) {
            return true;
        }

        if (obj instanceof CharSequence) {
            return StringUtils.isEmpty((CharSequence) obj);
        } else if (obj instanceof Map) {
            return MapUtils.isEmpty((Map) obj);
        } else if (obj instanceof Iterable) {
            return IterUtils.isEmpty((Iterable) obj);
        } else if (obj instanceof Iterator) {
            return IterUtils.isEmpty((Iterator) obj);
        } else if (ArrayUtils.isArray(obj)) {
            return ArrayUtils.isEmpty(obj);
        }

        return false;
    }

    /**
     * 判断指定对象是否为非空，支持：
     *
     * <pre>
     * 1. CharSequence
     * 2. Map
     * 3. Iterable
     * 4. Iterator
     * 5. Array
     * </pre>
     *
     * @param obj 被判断的对象
     *
     * @return 是否为空，如果类型不支持，返回 true
     */
    public static boolean isNotEmpty(Object obj) {
        return !isEmpty(obj);
    }

    /**
     * 如果给定对象为 {@code null} 返回默认值
     *
     * <pre>
     * ObjectUtil.defaultIfNull(null, null)      = null
     * ObjectUtil.defaultIfNull(null, "")        = ""
     * ObjectUtil.defaultIfNull(null, "zz")      = "zz"
     * ObjectUtil.defaultIfNull("abc", *)        = "abc"
     * ObjectUtil.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
     * </pre>
     *
     * @param <T> 对象类型
     * @param object 被检查对象，可能为 {@code null}
     * @param defaultValue 被检查对象为 {@code null} 返回的默认值，可以为 {@code null}
     *
     * @return 被检查对象为 {@code null} 返回默认值，否则返回原值
     */
    public static <T> T defaultIfNull(final T object, final T defaultValue) {
        return (null != object) ? object : defaultValue;
    }

    /**
     * 克隆对象<br>
     * 如果对象实现 Cloneable 接口，调用其 clone 方法<br>
     * 如果实现 Serializable 接口，执行深度克隆<br>
     * 否则返回<code>null</code>
     *
     * @param <T> 对象类型
     * @param obj 被克隆对象
     *
     * @return 克隆后的对象
     */
    public static <T> T clone(T obj) {
        T result = ArrayUtils.clone(obj);
        if (null == result) {
            if (obj instanceof Cloneable) {
                result = ReflectUtils.invoke(obj, "clone");
            } else {
                result = cloneByStream(obj);
            }
        }
        return result;
    }

    /**
     * 返回克隆后的对象，如果克隆失败，返回原对象
     *
     * @param <T> 对象类型
     * @param obj 对象
     *
     * @return 克隆后或原对象
     */
    public static <T> T cloneIfPossible(final T obj) {
        T clone = null;
        try {
            clone = clone(obj);
        } catch (Exception e) {
            // pass
        }
        return clone == null ? obj : clone;
    }

    /**
     * 序列化后拷贝流的方式克隆<br>
     * 对象必须实现 Serializable 接口
     *
     * @param <T> 对象类型
     * @param obj 被克隆对象
     *
     * @return 克隆后的对象
     *
     * @throws UtilsException IO 异常和 ClassNotFoundException 封装
     */
    public static <T> T cloneByStream(T obj) {
        if (!(obj instanceof Serializable)) {
            return null;
        }
        final FastByteArrayOutputStream byteOut = new FastByteArrayOutputStream();
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(byteOut);
            out.writeObject(obj);
            out.flush();
            final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
            return (T) in.readObject();
        } catch (Exception e) {
            throw new UtilsException(e);
        } finally {
            IoUtils.close(out);
        }
    }

    /**
     * 序列化<br>
     * 对象必须实现 Serializable 接口
     *
     * @param <T> 对象类型
     * @param obj 要被序列化的对象
     *
     * @return 序列化后的字节码
     */
    public static <T> byte[] serialize(T obj) {
        if (!(obj instanceof Serializable)) {
            return null;
        }

        FastByteArrayOutputStream byteOut = new FastByteArrayOutputStream();
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(byteOut);
            oos.writeObject(obj);
            oos.flush();
        } catch (Exception e) {
            throw new UtilsException(e);
        } finally {
            IoUtils.close(oos);
        }
        return byteOut.toByteArray();
    }

    /**
     * 反序列化<br>
     * 对象必须实现 Serializable 接口
     *
     * @param <T> 对象类型
     * @param bytes 反序列化的字节码
     *
     * @return 反序列化后的对象
     *
     * @see #unserialize(byte[])
     */
    public static <T> T deserialize(byte[] bytes) {
        return unserialize(bytes);
    }

    /**
     * 反序列化<br>
     * 对象必须实现 Serializable 接口
     *
     * @param <T> 对象类型
     * @param bytes 反序列化的字节码
     *
     * @return 反序列化后的对象
     */
    public static <T> T unserialize(byte[] bytes) {
        ObjectInputStream ois;
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bais);
            return (T) ois.readObject();
        } catch (Exception e) {
            throw new UtilsException(e);
        }
    }

    /**
     * 是否为基本类型，包括包装类型和非包装类型
     *
     * @param object 被检查对象
     *
     * @return 是否为基本类型
     *
     * @see ClassUtils#isBasicType(Class)
     */
    public static boolean isBasicType(Object object) {
        return ClassUtils.isBasicType(object.getClass());
    }

    /**
     * 检查是否为有效的数字<br>
     * 检查 Double 和 Float 是否为无限大，或者 Not a Number<br>
     * 非数字类型和 Null 将返回 true
     *
     * @param obj 被检查类型
     *
     * @return 检查结果，非数字类型和 Null 将返回 true
     */
    public static boolean isValidIfNumber(Object obj) {
        if (obj instanceof Number) {
            return NumberUtils.isValidNumber((Number) obj);
        }
        return true;
    }

    /**
     * {@code null} 安全的对象比较，{@code null} 对象排在末尾
     *
     * @param <T> 被比较对象类型
     * @param c1 对象1，可以为 {@code null}
     * @param c2 对象2，可以为 {@code null}
     *
     * @return 比较结果，如果 c1 &lt; c2，返回数小于 0，c1==c2 返回 0，c1 &gt; c2 大于 0
     *
     * @see Comparator#compare(Object, Object)
     */
    public static <T extends Comparable<? super T>> int compare(T c1, T c2) {
        return CompareUtils.compare(c1, c2);
    }

    /**
     * {@code null} 安全的对象比较
     *
     * @param <T> 被比较对象类型
     * @param c1 对象1，可以为 {@code null}
     * @param c2 对象2，可以为 {@code null}
     * @param nullGreater 当被比较对象为 null 时是否排在前面
     *
     * @return 比较结果，如果 c1 &lt; c2，返回数小于 0，c1==c2 返回 0，c1 &gt; c2 大于 0
     *
     * @see Comparator#compare(Object, Object)
     */
    public static <T extends Comparable<? super T>> int compare(T c1, T c2, boolean nullGreater) {
        return CompareUtils.compare(c1, c2, nullGreater);
    }

    /**
     * 获得给定类的第一个泛型参数
     *
     * @param obj 被检查的对象
     *
     * @return {@link Class}
     */
    public static Class<?> getTypeArgument(Object obj) {
        return getTypeArgument(obj, 0);
    }

    /**
     * 获得给定类的第一个泛型参数
     *
     * @param obj 被检查的对象
     * @param index 泛型类型的索引号，即第几个泛型类型
     *
     * @return {@link Class}
     */
    public static Class<?> getTypeArgument(Object obj, int index) {
        return ClassUtils.getTypeArgument(obj.getClass(), index);
    }

    /**
     * 将 Object 转为 String<br>
     * 策略为：
     * <pre>
     *  1、null 转为"null"
     *  2、调用 Convert.toStr(Object)转换
     * </pre>
     *
     * @param obj Bean 对象
     *
     * @return Bean 所有字段转为 Map 后的字符串
     */
    public static String toString(Object obj) {
        if (null == obj) {
            return StringUtils.NULL;
        }
        if (obj instanceof Map) {
            return obj.toString();
        }

        return Convert.toString(obj);
    }

    /**
     * 存在多少个 {@code null} 或空对象，通过 {@link ObjectUtils#isEmpty(Object)} 判断元素
     *
     * @param objs 被检查的对象,一个或者多个
     *
     * @return 存在 {@code null} 的数量
     */
    public static int emptyCount(Object... objs) {
        return ArrayUtils.emptyCount(objs);
    }

    /**
     * 是否存在 {@code null} 或空对象，通过 {@link ObjectUtils#isEmpty(Object)} 判断元素
     *
     * @param objs 被检查对象
     *
     * @return 是否存在
     */
    public static boolean hasEmpty(Object... objs) {
        return ArrayUtils.hasEmpty(objs);
    }

    /**
     * 是否存都为 {@code null} 或空对象，通过 {@link ObjectUtils#isEmpty(Object)} 判断元素
     *
     * @param objs 被检查的对象,一个或者多个
     *
     * @return 是否都为空
     */
    public static boolean isAllEmpty(Object... objs) {
        return ArrayUtils.isAllEmpty(objs);
    }

    /**
     * 是否存都不为 {@code null} 或空对象，通过 {@link ObjectUtils#isEmpty(Object)} 判断元素
     *
     * @param objs 被检查的对象,一个或者多个
     *
     * @return 是否都不为空
     */
    public static boolean isAllNotEmpty(Object... objs) {
        return ArrayUtils.isAllNotEmpty(objs);
    }

}
